#pragma once

#include "log.hpp"
#include <iostream>
#include <cstdint>
#include <cstring>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <ctime>
#include <functional>
#include <unordered_map>

extern Log lg;

enum
{
    SOCKET_ERR = 1,
    BIND_ERR
};

using CallBack = std::function<std::string(const std::string &)>;

class updserver
{
private:
    int fd_;
    uint16_t port_;
    std::unordered_map<std::string, struct sockaddr_in> users; // 维护"在线"用户，使用ip地址作为key

public:
    updserver(uint16_t port) : port_(port) {}

public:
    void Init()
    {
        // 创建套接字
        fd_ = socket(AF_INET, SOCK_DGRAM, 0);
        if (fd_ < 0)
        {
            lg(Fatal, "socket create failed,fd: %d", fd_);
            exit(SOCKET_ERR);
        }

        // bind
        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(port_);
        local.sin_addr.s_addr = 0;
        socklen_t len = sizeof(local);
        int ret = bind(fd_, (struct sockaddr *)&local, len);
        if (ret < 0)
        {
            lg(Fatal, "server bind failed, err string: %s", strerror(errno));
            exit(BIND_ERR);
        }
    }

    void Start()
    {
        while (true)
        {
            char buf[1024] = {0};
            struct sockaddr_in client;
            socklen_t len = sizeof(client);

            // udp中 redvfrom返回值为0的情况代表对方发送了一个空的数据包，而在tcp中recv返回值为0的情况代表对方关闭了链接
            int n = recvfrom(fd_, buf, sizeof(buf) - 1, 0, (struct sockaddr *)&client, &len);
            if (n < 0)
            {
                // 出错了
                lg(Error, "server recvfrom error");
                continue;
            }
            buf[n] = 0;

            // 获取客户端的IP和端口号
            std::string clientip = inet_ntoa(client.sin_addr);
            uint16_t clientport = ntohs(client.sin_port);

            // 判断用户是否在线
            auto it = users.find(clientip);
            if (it == users.end())
            {
                // 不在线则加入
                users.insert({clientip, client});
                lg(Info, "[client_ip: %s:client_port: %d] User online", clientip.c_str(), clientport);
            }

            lg(Info, "server recv a message:%s,[client_ip: %s:client_port: %d]", buf, clientip.c_str(), clientport);

            string echo_string;
            echo_string+="[";
            echo_string+=clientip;
            echo_string+=":";
            echo_string+=to_string(clientport);
            echo_string+="] ";
            echo_string+=buf;

            // 发送响应报文,需要"群发"
            for (auto &s : users)
            {
                // if(s.first == clientip){
                //     //是自己发的消息
                //     std::string msg = "本机";
                //     msg += echo_string;
                //     sendto(fd_, msg.c_str(), msg.size(), 0, (struct sockaddr *)&s.second, len);
                //     continue;
                // }

                n = sendto(fd_, echo_string.c_str(), echo_string.size(), 0, (struct sockaddr *)&s.second, len);
                if (n < 0)
                {
                    lg(Error, "server sendto error");
                    users.erase(s.first);
                    continue;
                }
            }
        }
    }
};